/** * Copyright (c) 2009, iPlant Collaborative, Texas Advanced Computing Center This software is licensed * under the CC-GNU GPL version 2.0 or later. License: http://creativecommons.org/licenses/GPL/2.0/ */ package org.iplantc.phyloviewer.viewer.client; import org.iplantc.phyloviewer.shared.math.Box2D; import org.iplantc.phyloviewer.shared.model.Document; import org.iplantc.phyloviewer.shared.render.Defaults; import org.iplantc.phyloviewer.shared.render.RenderPreferences; import org.iplantc.phyloviewer.shared.render.style.BranchStyle; import org.iplantc.phyloviewer.shared.render.style.CompositeStyle; import org.iplantc.phyloviewer.shared.render.style.GlyphStyle; import org.iplantc.phyloviewer.shared.render.style.LabelStyle; import org.iplantc.phyloviewer.shared.render.style.NodeStyle; import org.iplantc.phyloviewer.viewer.client.TreeWidget.ViewType; import org.iplantc.phyloviewer.viewer.client.services.CombinedServiceAsync; import org.iplantc.phyloviewer.viewer.client.services.CombinedServiceAsyncImpl; import org.iplantc.phyloviewer.viewer.client.services.SearchServiceAsyncImpl; import org.iplantc.phyloviewer.viewer.client.services.TreeListService; import org.iplantc.phyloviewer.viewer.client.services.TreeListServiceAsync; import org.iplantc.phyloviewer.viewer.client.services.CombinedService.NodeResponse; import org.iplantc.phyloviewer.viewer.client.services.SearchServiceAsyncImpl.RemoteNodeSuggestion; import org.iplantc.phyloviewer.viewer.client.style.StyleByLabel; import org.iplantc.phyloviewer.viewer.client.ui.BranchStyleWidget; import org.iplantc.phyloviewer.viewer.client.ui.ColorBox; import org.iplantc.phyloviewer.viewer.client.ui.ContextMenu; import org.iplantc.phyloviewer.viewer.client.ui.GlyphStyleWidget; import org.iplantc.phyloviewer.viewer.client.ui.LabelStyleWidget; import org.iplantc.phyloviewer.viewer.client.ui.NodeStyleWidget; import org.iplantc.phyloviewer.viewer.client.ui.NodeTable; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.logical.shared.HasValueChangeHandlers; import com.google.gwt.event.logical.shared.SelectionEvent; import com.google.gwt.event.logical.shared.SelectionHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.EventBus; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.event.shared.SimpleEventBus; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.MenuBar; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.RootLayoutPanel; import com.google.gwt.user.client.ui.SuggestBox; import com.google.gwt.user.client.ui.TextArea; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; /** * Entry point classes define <code>onModuleLoad()</code>. */ public class Phyloviewer implements EntryPoint { TreeWidget widget; JSTreeList trees; CombinedServiceAsync combinedService = new CombinedServiceAsyncImpl(); SearchServiceAsyncImpl searchService = new SearchServiceAsyncImpl(); TreeListServiceAsync treeList = GWT.create(TreeListService.class); EventBus eventBus = new SimpleEventBus(); /** * This is the entry point method. */ public void onModuleLoad() { widget = new TreeWidget(searchService, eventBus); CompositeStyle highlightStyle = new CompositeStyle("highlight", Defaults.DEFAULT_STYLE); highlightStyle.setNodeStyle(new NodeStyle("#C2C2F5", Double.NaN)); highlightStyle.setLabelStyle(new LabelStyle(null)); highlightStyle.setGlyphStyle(new GlyphStyle(null, "#C2C2F5", Double.NaN)); highlightStyle.setBranchStyle(new BranchStyle("#C2C2F5", Double.NaN)); RenderPreferences rp = new RenderPreferences(); rp.setHighlightStyle(highlightStyle); widget.setRenderPreferences(rp); MenuBar fileMenu = new MenuBar(true); fileMenu.addItem("Open...", new Command() { @Override public void execute() { Phyloviewer.this.displayTrees(); } }); Command openURL = new Command() { @Override public void execute() { Window.open(widget.exportImageURL(), "_blank", ""); } }; fileMenu.addItem("Get image (opens in a popup window)", openURL); MenuBar layoutMenu = new MenuBar(true); layoutMenu.addItem("Rectangular", new Command() { @Override public void execute() { widget.setViewType(TreeWidget.ViewType.VIEW_TYPE_CLADOGRAM); } }); layoutMenu.addItem("Circular", new Command() { @Override public void execute() { widget.setViewType(TreeWidget.ViewType.VIEW_TYPE_RADIAL); } }); MenuBar styleMenu = new MenuBar(true); final TextInputPopup styleTextPopup = new TextInputPopup(); styleTextPopup.addValueChangeHandler(new ValueChangeHandler<String>() { @Override public void onValueChange(ValueChangeEvent<String> event) { StyleByLabel styleMap = new StyleByLabel(); styleMap.put(event.getValue()); widget.getView().getDocument().setStyleMap(styleMap); widget.render(); } }); styleTextPopup.setModal(true); styleMenu.addItem("Style by CSV", new Command() { @Override public void execute() { styleTextPopup.setPopupPositionAndShow(new PopupPanel.PositionCallback() { @Override public void setPosition(int offsetWidth, int offsetHeight) { int left = (Window.getClientWidth() - offsetWidth) / 3; int top = (Window.getClientHeight() - offsetHeight) / 3; styleTextPopup.setPopupPosition(left, top); } }); } }); // Make a search box final SuggestBox searchBox = new SuggestBox(searchService); searchBox.setLimit(10); // TODO make scrollable? searchBox.addSelectionHandler(new SelectionHandler<Suggestion>() { @Override public void onSelection(SelectionEvent<Suggestion> event) { Box2D box = ((RemoteNodeSuggestion)event.getSelectedItem()).getResult().layout.boundingBox; widget.show(box); } }); // create some styling widgets for the context menu NodeStyleWidget nodeStyleWidget = new NodeStyleWidget(widget.getView().getDocument()); BranchStyleWidget branchStyleWidget = new BranchStyleWidget(widget.getView().getDocument()); GlyphStyleWidget glyphStyleWidget = new GlyphStyleWidget(widget.getView().getDocument()); LabelStyleWidget labelStyleWidget = new LabelStyleWidget(widget.getView().getDocument()); // replace their default TextBoxes with ColorBoxes, which jscolor.js will add a color picker to nodeStyleWidget.setColorWidget(createColorBox()); branchStyleWidget.setStrokeColorWidget(createColorBox()); glyphStyleWidget.setStrokeColorWidget(createColorBox()); glyphStyleWidget.setFillColorWidget(createColorBox()); labelStyleWidget.setColorWidget(createColorBox()); // add the widgets to separate panels on the context menu final ContextMenu contextMenuPanel = new ContextMenu(widget); contextMenuPanel.add(new NodeTable(), "Node details", 3); contextMenuPanel.add(nodeStyleWidget, "Node", 3); contextMenuPanel.add(branchStyleWidget, "Branch", 3); contextMenuPanel.add(glyphStyleWidget, "Glyph", 3); contextMenuPanel.add(labelStyleWidget, "Label", 3); HorizontalPanel searchPanel = new HorizontalPanel(); searchPanel.add(new Label("Search:")); searchPanel.add(searchBox); // Make the UI. MenuBar menu = new MenuBar(); final DockLayoutPanel mainPanel = new DockLayoutPanel(Unit.EM); mainPanel.addNorth(menu, 2); mainPanel.addSouth(searchPanel, 2); mainPanel.addWest(contextMenuPanel, 0); mainPanel.add(widget); RootLayoutPanel.get().add(mainPanel); MenuBar viewMenu = new MenuBar(true); viewMenu.addItem("Layout", layoutMenu); viewMenu.addItem("Style", styleMenu); contextMenuPanel.setVisible(false); viewMenu.addItem("Toggle Context Panel", new Command() { @Override public void execute() { if(contextMenuPanel.isVisible()) { contextMenuPanel.setVisible(false); mainPanel.setWidgetSize(contextMenuPanel, 0); mainPanel.forceLayout(); } else { contextMenuPanel.setVisible(true); mainPanel.setWidgetSize(contextMenuPanel, 20); mainPanel.forceLayout(); } } }); menu.addItem("File", fileMenu); menu.addItem("View", viewMenu); // Draw for the first time. RootLayoutPanel.get().forceLayout(); mainPanel.forceLayout(); widget.setViewType(ViewType.VIEW_TYPE_CLADOGRAM); widget.render(); initColorPicker(); String treeIdString = Window.Location.getParameter("treeId"); if(treeIdString != null && !treeIdString.equals("")) { int treeId = Integer.parseInt(treeIdString); this.loadTree(null, treeId); } else { // Present the user the dialog to load a tree. this.displayTrees(); } } private ColorBox createColorBox() { ColorBox colorBox = new ColorBox(); colorBox.addStyleName("{hash:true,required:false}"); // jscolor config return colorBox; } private final native void initColorPicker() /*-{ $wnd.jscolor.init(); }-*/; private void loadTree(final PopupPanel displayTreePanel, final int treeId) { combinedService.getRootNode(treeId, new AsyncCallback<NodeResponse>() { @Override public void onFailure(Throwable arg0) { Window.alert(arg0.getMessage()); if(displayTreePanel != null) { displayTreePanel.hide(); } } @Override public void onSuccess(NodeResponse response) { Document document = new PagedDocument(combinedService, eventBus, treeId, response); searchService.setTree(document.getTree()); widget.setDocument(document); if(displayTreePanel != null) { displayTreePanel.hide(); } } }); } private void displayTrees() { final PopupPanel displayTreePanel = new PopupPanel(); displayTreePanel.setModal(true); displayTreePanel.setPopupPositionAndShow(new PopupPanel.PositionCallback() { public void setPosition(int offsetWidth, int offsetHeight) { int left = (Window.getClientWidth() - offsetWidth) / 2; int top = (Window.getClientHeight() - offsetHeight) / 2 - 100; displayTreePanel.setPopupPosition(left, top); } }); final VerticalPanel vPanel = new VerticalPanel(); displayTreePanel.add(vPanel); Label messageLabel = new Label("Select a tree to load:"); vPanel.add(messageLabel); final Label label = new Label("Retrieving tree list..."); vPanel.add(label); final ListBox lb = new ListBox(); lb.setVisible(false); vPanel.add(lb); final HorizontalPanel hPanel = new HorizontalPanel(); hPanel.add(new Button("OK", new ClickHandler() { @Override public void onClick(ClickEvent arg0) { label.setText("Loading..."); label.setVisible(true); lb.setVisible(false); hPanel.setVisible(false); int index = lb.getSelectedIndex(); if(index >= 0 && trees != null) { final JSTreeData data = trees.getTree(index); if(data != null) { loadTree(displayTreePanel, data.getId()); } } } })); hPanel.add(new Button("Cancel", new ClickHandler() { @Override public void onClick(ClickEvent arg0) { displayTreePanel.hide(); } })); vPanel.add(hPanel); displayTreePanel.show(); treeList.getTreeList(new AsyncCallback<String>() { @Override public void onFailure(Throwable arg0) { Window.alert(arg0.getMessage()); } @Override public void onSuccess(String json) { trees = JSTreeList.parseJSON(json); for(int i = 0;i < trees.getNumberOfTrees();++i) { lb.addItem(trees.getTree(i).getName()); } label.setVisible(false); lb.setVisible(true); } }); } private class TextInputPopup extends PopupPanel implements HasValueChangeHandlers<String> { public TextInputPopup() { VerticalPanel vPanel = new VerticalPanel(); final TextArea textBox = new TextArea(); textBox.setVisibleLines(20); textBox.setCharacterWidth(80); Button okButton = new Button("OK", new ClickHandler() { @Override public void onClick(ClickEvent event) { ValueChangeEvent.fire(TextInputPopup.this, textBox.getValue()); TextInputPopup.this.hide(); } }); vPanel.add(textBox); vPanel.add(okButton); this.add(vPanel); } @Override public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) { return addHandler(handler, ValueChangeEvent.getType()); } } }